jqUnit.module("PASS");

jqUnit.test("without shortcut", function() {    
	var myObj = {sayHello:function(){}};
    var mock = new jqMock.Mock(myObj, "sayHello");
    mock.modify().args(jqMock.is.anyOf(["Lisa","Bart","Maggie"]));
    mock.modify().args("Homer").multiplicity(jqMock.expect.atLeast(1));    
    myObj.sayHello("Lisa");    
    myObj.sayHello("Homer");    
    mock.verifyAll();
});

jqMock.addShortcut();

jqUnit.test("mocking native dialog", function() {
	var confirmMock = new jqMock.Mock(window, "confirm");
	confirmMock.modify().args("are you sure?").returnValue(true);
	confirmMock.modify().args("are you sure 2?").returnValue(false);
	var b1 = confirm("are you sure?");
	jqUnit.ok(b1, "should be true");
	var b2 = confirm("are you sure 2?");
	jqUnit.ok(!b2, "should be true (2)");	
	confirmMock.verifyAll();
	confirmMock.restore();
	
	var alertMock = new jqMock.Mock(window, "alert");
	alertMock.modify().args("are you sure?").returnValue();
	alert("are you sure?");
	alertMock.verifyAll();
	alertMock.restore();
});

jqUnit.test("args() expectation", function() {

	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	
	mock.modify().args(16);
	mock.modify().args([4,5,6]);
	var obj = {a:'a', b: ['x', 'y'], func : function() {}};
	mock.modify().args(obj);
	mock.modify().args(null);
	mock.modify().args(undefined);
	mock.modify().args(16,[4,5,6], obj);
	mock.modify(); // default no arg, multiplicity 1
	
	var objActual = {a:3, fn:function(){}, b:{x:'x',y:'y'}};
	var objExpect = {b:{x:'x',y:'y'}, a:3, fn:is.anything};
	mock.modify().args(objExpect);
	myobj.fn(objActual);
	
	myobj.fn(16);
	myobj.fn([4,5,6]);
	myobj.fn(obj);
	myobj.fn(null);
	myobj.fn();
	var b;
	myobj.fn(b);
	myobj.fn(16,[4,5,6], obj);
	
	mock.verifyAll();
});

jqUnit.test("Expression is()", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	
	mock.modify().args(is.anything).multiplicity(4);
	myobj.fn("a");
	myobj.fn(3);
	myobj.fn([1,2,3]);
	myobj.fn({a:[1,2,3],b:{c:'c'}});
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args({a:'apple', x: is.anything, b:'banana'}).multiplicity(2);
	myobj.fn({a:'apple', x: 'xxx', b:'banana'});
	myobj.fn({a:'apple', b:'banana'});
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.not('x')).multiplicity(4);
	myobj.fn("y");
	myobj.fn(1);
	myobj.fn(['x','y']);
	myobj.fn({x:'x', y:'y'});
	mock.verifyAll();
	mock.reset();
	
	var MyObject = function MyObject(){};
	mock.modify().args(is.instanceOf(MyObject));
	mock.modify().args(is.instanceOf(Array));
	myobj.fn(new MyObject());
	myobj.fn([1,2,3]);
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.not(null));
	mock.modify().args(is.not(undefined));	
	myobj.fn("something");
	myobj.fn(null);
	mock.verifyAll();
	mock.reset();	
	
	mock.modify().args(is.objectThatIncludes({a:'apple', b:'banana'}));
	myobj.fn({a:'apple', fn:function(){}, b:'banana'});
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.regex(/^abc[0-9]*xyz$/));
	mock.modify().args(is.not(is.regex(/[0-9]/)));
	myobj.fn("abc12345xyz");
	myobj.fn("abc");
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.anyOf(["a", "b", "c",null, undefined])).multiplicity(5);
	myobj.fn("a");
	myobj.fn(null);
	myobj.fn("c");
	myobj.fn(undefined);
	myobj.fn("b");
	mock.verifyAll();
	mock.reset();	
	
	mock.modify().args(is.allOf([is.not("a"), is.not("b")])).multiplicity(2);
	mock.modify().args(is.allOf([is.not(null), is.not(undefined)]));
	myobj.fn("x");
	myobj.fn("y");
	myobj.fn("z");
	mock.verifyAll();
	mock.reset();
		
	var isEven = function(arg) {
		return arg % 2 == 0 ;
	};
	mock.modify().args(is.custom(isEven)).multiplicity(3);
	myobj.fn(2);
	myobj.fn(4);
	myobj.fn(6);
	mock.verifyAll();
	mock.reset();	
});

jqUnit.test("expectation resolution", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	
	mock.modify().args(is.not('x'));
	mock.modify().args(is.not('y')); 
	mock.modify().args(is.anything);
	myobj.fn(1);
	myobj.fn(2);
	myobj.fn(3);
	mock.verifyAll();
	mock.reset();
	
	// this is an RMock behaviour.
	mock.modify().multiplicity(2).args(is.not("y"));
	mock.modify().multiplicity(expect.atLeast(2)).args("x");
	for(var i=0; i<4; i++) myobj.fn("x");
	mock.verifyAll();
	mock.reset();
	
	mock.modify().multiplicity(expect.times(2,3)).args("x");
	mock.modify().multiplicity(expect.exactly(1)).args(is.not("y"));
	mock.modify().multiplicity(expect.atMost(1)).args("x");
	for(var i=0; i<5; i++) myobj.fn("x");
	mock.verifyAll();
	mock.reset();	
});

jqUnit.test("ordered expectations", function() {

	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	mock.setOrdered(true);
	
	mock.modify().args(1);
	mock.modify().args(2);
	mock.modify().args(3);
	myobj.fn(1);
	myobj.fn(2);
	myobj.fn(3);
	mock.verifyAll();
	mock.reset();	
	
	mock.modify().args(4);
	myobj.fn(4);
	mock.verifyAll();
	mock.reset();	
	
	mock.modify().args('x');
	mock.modify().args('y');
	mock.modify().args('x');
	myobj.fn('x');
	myobj.fn('y');
	myobj.fn('x');
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.anything);
	mock.modify().args(is.not('y'));
	mock.modify().args(is.instanceOf(Array));
	myobj.fn({});
	myobj.fn('x');
	myobj.fn([1,2,3]);
	mock.verifyAll();
	mock.reset();		
});

jqUnit.test("unexpected invocation, but using verify() only", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");	
	mock.modify().args(1);
	mock.modify().args(2);
	mock.modify().args(3);
	myobj.fn(3);
	myobj.fn(2);
	myobj.fn(5);  // unexpected, but we dont check
	myobj.fn(4);  // unexpected, but we dont check
	myobj.fn(1);
	mock.verify();
});

jqUnit.test("returnValue and intercepting", function() {
	var myobj = {sum:function(a,b){return a+b;}};
	var mock = new jqMock.Mock(myobj, "sum");
	mock.modify().args(5,6).returnValue(100);  // change the returnValue
	mock.modify().args(5,6); // don't intercept
	jqUnit.equals(myobj.sum(5,6) , 100);
	jqUnit.equals(myobj.sum(5,6) , 11);
	mock.restore();
	jqUnit.equals(myobj.sum(11,12) , 23);
});

jqUnit.test("throwException", function() {
	jqUnit.expect(2);
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	mock.modify().args().throwException("my exception");
	var myError = new Error("some error");
	mock.modify().args(1).throwException(myError);	
	try {
		myobj.fn();
	} catch (ex) {
		jqUnit.equals(ex, "my exception");
	}
	try {
		myobj.fn(1);
	} catch (ex) {
		jqUnit.equals(ex.message, "some error");
	}	
});

jqUnit.test("multiplicity", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");

	mock.modify().args().multiplicity(expect.exactly(1));
	mock.modify().args('a').multiplicity(expect.times(3));
	mock.modify().args('c').multiplicity(2);
	mock.modify().args('b','bbb').multiplicity(expect.times(2,3));
	mock.modify().args({p:'p'},[7,8]);
	mock.modify().args('y').multiplicity(expect.atLeast(1));
	mock.modify().args('z').multiplicity(expect.atMost(4));	
	mock.modify().args('foo').multiplicity(expect.atMost(0));
	mock.modify().args('zero1').multiplicity(0);
	mock.modify().args('zero2').multiplicity(expect.exactly(0));
	mock.modify().args('zero3').multiplicity(expect.times(0,2));

	myobj.fn({p:'p'},[7,8]);
	myobj.fn('a');
	myobj.fn();
	myobj.fn('a');		
	myobj.fn('a');
	myobj.fn('c');
	myobj.fn('c');
	myobj.fn('b','bbb');
	myobj.fn('z');	
	myobj.fn('y');	
	myobj.fn('y');	
	myobj.fn('b','bbb');
	myobj.fn('z');	
	myobj.fn('z');	
	myobj.fn('z');	
	mock.verifyAll();
	mock.reset();
});

jqUnit.test("tostring", function() {
	jqUnit.equals(jqMock.tostring(3), "3");
	jqUnit.equals(jqMock.tostring("a"), "'a'");
	jqUnit.equals(jqMock.tostring([]), "[]");
	jqUnit.equals(jqMock.tostring([1,2,3]), "[1,2,3]");
	jqUnit.equals(jqMock.tostring([1,2,"x"]), "[1,2,'x']");	
	jqUnit.equals(jqMock.tostring(function(){}), "function()");
	jqUnit.equals(jqMock.tostring({}), "{}");
	jqUnit.equals(jqMock.tostring({a:1,b:2,c:'cc'}), "{a:1,b:2,c:'cc'}");
	var x;
	jqUnit.equals(jqMock.tostring(x), "undefined");
	jqUnit.equals(jqMock.tostring(null), "null");

	jqUnit.equals(jqMock.tostring(is.anything), "is.anything");
	jqUnit.equals(jqMock.tostring(is.not('x')), "is.not('x')");
	jqUnit.equals(jqMock.tostring(is.instanceOf(Array)), "is.instanceOf(Array)");
	jqUnit.equals(jqMock.tostring(is.objectThatIncludes({a:'a'})), "is.objectThatIncludes({a:'a'})");
	jqUnit.equals(jqMock.tostring(is.regex(/^[0-9]*/)), "is.regex(/^[0-9]*/)");
	jqUnit.equals(jqMock.tostring(is.anyOf([is.not('a'), is.not('b')])), "is.anyOf([is.not('a'),is.not('b')])");
	jqUnit.equals(jqMock.tostring(is.allOf([is.not(null), is.not(undefined)])), "is.allOf([is.not(null),is.not(undefined)])");
	jqUnit.equals(jqMock.tostring(is.exception({name: "ReferenceError", message: "blah is not defined", type: ReferenceError})), "is.exception({name:'ReferenceError',message:'blah is not defined',type:is.instanceOf(ReferenceError)})");
	jqUnit.equals(jqMock.tostring(is.exception("foo")), "is.exception({message:'foo'})");
	
	// NOTE: rhino has error during for(var i : obj) which returns the wrong ordering due to HashMap implementation
	var obj = {c:{x:'x',y:['xx',99]}, d:[1,2,{p:'p',q:5}], fn:function(){}, a:3, b:'bob'};
	var objStr = "{c:{x:'x',y:['xx',99]},d:[1,2,{p:'p',q:5}],fn:function(),a:3,b:'bob'}"
	jqUnit.equals(jqMock.tostring(obj), objStr);
});

jqUnit.test("circular reference", function() {
	var a={}; 
	var b={};
	var x1={'x':a};
	var x2={'x':b};
	a['x'] = x1;		 
	b['x'] = x2;

	var aa=[]; 
	var bb=[];
	var xx1=[aa];
	var xx2=[bb];
	aa[0] = xx1;		 
	bb[0] = xx2;			
			
	jqUnit.expect(6);

	jqUnit.equals(jqMock.tostring(a), "{x:{x:...}}");
	jqUnit.equals(jqMock.tostring(aa), "[[...]]");
		
	try {
		jqMock.assertThat(a,b);
	} catch (ex) {
		jqUnit.equals(ex.message, "Circular Reference detected in the object!", "ciruclar ref in object");
	}
	try {
		jqMock.assertThat(a,b);
	} catch (ex) {
		jqUnit.equals(ex.message, "Circular Reference detected in the object!", "ciruclar ref in object");
	}
	try {
		var myobj = {fn:function(){}};
		var mock = new jqMock.Mock(myobj, "fn");
		mock.modify().args(a).multiplicity(1);
		myobj.fn(b);
	} catch (ex) {
		jqUnit.equals(ex.message, "Circular Reference detected in the object!", "ciruclar ref in object");
	}
	try {
		var myobj = {fn:function(){}};
		var mock = new jqMock.Mock(myobj, "fn");
		mock.modify().args(aa).multiplicity(1);
		myobj.fn(bb);
	} catch (ex) {
		jqUnit.equals(ex.message, "Circular Reference detected in the object!", "ciruclar ref in object");
	}
});

jqUnit.test("object assertion", function() {
	jqMock.assertThat(window, window, "assertThat(window), reference equality");
	jqMock.assertThat(3,3);
	jqMock.assertThat("a","a");
	jqMock.assertThat([1,2,3],[1,2,3]);	
	jqMock.assertThat({},{}, "{}");
	jqMock.assertThat([{a:'a'},{b:'b',d:'d'}],[{a:'a'},{d:'d', b:'b'}]);
	jqMock.assertThat({b:'b',x:[1,2,3],d:'d'},{d:'d',x:[1,2,3],b:'b'});
	var fn = function(n) {return n+1;};
	jqMock.assertThat(fn, fn);
	
	var objActual = {a:3, b:{x:'x',y:'y'}, c:[7,8,9]};
	var objExpect = {b:{x:'x',y:'y'}, c:[7,8,9], a:3};
	jqMock.assertThat(objActual,objExpect);
	
	jqMock.assertThat([1,2,3], is.instanceOf(Array));
	jqMock.assertThat("foo", is.anyOf(['foo','bar']));
	jqMock.assertThat({x:'x', a:'a', y:'y'}, is.objectThatIncludes({a:'a'}));
	
	jqMock.assertThat(new Error("booya"), is.exception("booya"), "is.exception");
});

jqUnit.test("expectThatExceptionThrown", function() {
	// simple compare
	jqMock.expectThatExceptionThrown(function() {
		throw "blah";
	}, "blah");
	// using expression
	jqMock.expectThatExceptionThrown(function() {
		throw new Error("booya");
	}, is.allOf([is.instanceOf(Error), is.objectThatIncludes({'message':'booya'})]));	
	// using is.exception expression
	jqMock.expectThatExceptionThrown(function() {
		throw {"message":"bad exception"};
	}, is.exception({message: "bad exception"}));	
	jqMock.expectThatExceptionThrown(function() {
		throw new Error("bad exception 2");
	}, is.exception("bad exception 2"));		
	jqMock.expectThatExceptionThrown(function() {
		eval("blah");
	}, is.exception());
	function MyUserException (message) {
	  this.message=message;
	  this.name="UserException";
	}
	jqMock.expectThatExceptionThrown(function() {
		throw new MyUserException("booya");
	}, is.exception({name: "UserException", message: "booya", type: MyUserException}));
	// just that some expression is thrown
	jqMock.expectThatExceptionThrown(function() {
		throw new Error("anything");
	}, is.exception());	
});

jqUnit.test("error handling", function() {
	jqUnit.expect(15);
	var obj = {x:'x'};
	try {
		new jqMock.Mock(obj, "x");
	} catch(ex) {
		jqUnit.equals(ex.message, "Could not create Mock: x is not a function", "exception should be thrown");
	}
	var obj2;
	try {
		new jqMock.Mock(obj2, "x");
	} catch(ex) {
		jqUnit.equals( ex.message, "Could not create Mock: the object or function you specified is not defined, obj.x", "exception should be thrown");
	}	
	var obj3 = {};
	try {
		new jqMock.Mock(obj3, "x");
	} catch(ex) {
		jqUnit.equals(ex.message, "Could not create Mock: the object or function you specified is not defined, obj.x", "exception should be thrown");
	}
	var obj4 = {fn:function(){}};
	try {
		var mock = new jqMock.Mock(obj4, "fn");
		mock.verify();
	} catch(ex) {
		jqUnit.equals(ex.message, "Expectation setup error. You must set up some expectations", "exception should be thrown");
	}
	var alertMock = new jqMock.Mock(window, "alert");
	try {		
		jqUnit.ok(window.alert.isMock, "flag should be set");
		new jqMock.Mock(window, "alert");		
	} catch(ex) {
		jqUnit.equals(ex.message, "Could not create Mock: you tried to overwrite a function which is already a mock!", "exception should be thrown");
	}
	alertMock.restore();
	
	var obj5 = {fn:function(){}};
	var mock5 = new jqMock.Mock(obj5, "fn");
	try {
		mock5.modify().args(is.instanceOf('x'));
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument to is.instanceOf() , argument must be a Class", "exception should be thrown");
	}
	try {
		mock5.modify().args(is.regex('x'));
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument to is.regex() , argument must be regex", "exception should be thrown");
	}
	try {
		mock5.modify().args(is.anyOf('x'));
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument to is.anyOf() , argument must be an Array", "exception should be thrown");
	}
	try {
		mock5.modify().args(is.allOf('x'));
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument to is.allOf() , argument must be an Array", "exception should be thrown");
	}
	try {
		mock5.modify().args(is.custom('x'));
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument to is.custom() , argument must be a Function that returns a boolean value", "exception should be thrown");
	}
	try {
		mock5.modify().multiplicity("x");
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument for multiplicity()", "exception should be thrown");
	}
		
	try {
		jqMock.addShortcut();
	} catch(ex) {
		jqUnit.equals(ex.message, "Cannot add shortcuts, expect() or is() already exists in global scope!", "exception should be thrown");	
	}	

	try {
		is.exception(0);
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument to is.exception() , argument must be an object or string");
	}
	
	try {
		is.exception({type:'foo'});
	} catch(ex) {
		jqUnit.equals(ex.message, "Invalid argument to is.exception() , the type attribute of the parameter must be a Class");
	}
});

//====================================================================
// The following tests check the failure behaviour of jqMock.
// After running tests that record failures through the framework,
// this code will scrape the resulting HTML to check the failures.
// Note: does not support the drilldown doubleclick view
//====================================================================

var counter = 15; // number of tests in PASS module above

var assertFailureCallbacks = []
function assertFailure(callback) {
	assertFailureCallbacks.push(callback);
}
function assertFailMessages(expectedMessages) {
	var allAssertionsPassed = true;
	var createSpan = function(passes) {
		return $("<span></span>")
			.css("color", passes ? "green" : "#FF6E1F")
			.text(passes? " [PASS]" : " [FAIL]");
	};
	var testResult = $('ol#tests>li').eq(counter++);	
	var assertionResults = testResult.find("ol>li");
	assertionResults.each(function(i,x) {
		var tmp = document.createElement("div");
		tmp.innerHTML = expectedMessages[i];  // because of HTML entities
		var ok = (x.innerHTML==tmp.innerHTML);
		if (!ok) {
			allAssertionsPassed = false;
			if (typeof console != "undefined" && console.error) {
				console.error("[" + counter + "] Actual:\n" + x.innerHTML);
				console.error("[" + counter + "] Expected:\n" + tmp.innerHTML);
			}
		}
		$(x).append(createSpan(ok));
		tmp = null;
	});
	createSpan(allAssertionsPassed).appendTo(testResult.find("strong"));
}
$(function() {
	for(var i=0; i<assertFailureCallbacks.length; i++) {
		assertFailureCallbacks[i]();
	}
	// scrape HTML to find fails, ignoring expected failures
	var failed = $('ol#tests>li.fail').filter(function(){
		return $(this).find('strong').text().indexOf(") [PASS]")<0;
	});
	$('h2#banner')[0].className = (failed.length>0 ? "fail" : "pass");
});


var unos = "Unordered expectations have not yet been satisfied:<br>";
var onos = "Ordered expectations have not yet been satisfied:<br>";
var noex = "No expectation matched for the following calls:<br>";
var noex2 = "No expectation matched for the following calls which were setup as expectations:<br>"
var br = "<br>";
var s3 = "&nbsp;&nbsp;&nbsp;";


// ----------------------  run the tests ----------------------
jqUnit.module("FAIL");

jqUnit.test("mocking native dialog", function() {
	var alertMock = new jqMock.Mock(window, "alert");
	alertMock.modify().args("some alert");
	window.alert("some other value");		
	alertMock.verifyAll();
	alertMock.restore();
});
assertFailure(function() {
	var expected = [];
	expected[0] = unos +
		"&rarr;0 (1) alert('some alert')" + br +
		noex +
		"alert('some other value')" + br;
	assertFailMessages(expected);
});


jqUnit.test("args() expectation", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	
	var objActual = {a:3, fn:function(){}, b:{x:'x',y:'y'}};
	var objExpect = {b:{x:'x',y:'zzzzz'}, a:3};
	mock.modify().args(objExpect);
	mock.modify().args(1,2,3);
	mock.modify().args();
	
	myobj.fn(objActual);
	myobj.fn(1,2);
	myobj.fn('foo');
			
	mock.verifyAll();
});
assertFailure(function() {
	var expected = [];
	expected[0] = unos +
		"&rarr;0 (1) fn({b:{x:'x',y:'zzzzz'},a:3})" +br+
		"&rarr;0 (1) fn(1,2,3)"+br+
		"&rarr;0 (1) fn()"+br+		
		noex+
		"fn({a:3,fn:function(),b:{x:'x',y:'y'}})"+br+
		"fn(1,2)"+br+
		"fn('foo')"+br;
	assertFailMessages(expected);
});


jqUnit.test("Expression is()", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");

	mock.modify().args(is.not('x'));
	mock.verifyAll();
	mock.reset();
	
	var MyObject = function MyObject(){};
	mock.modify().args(is.instanceOf(MyObject));
	mock.modify().args(is.instanceOf(Array));
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.objectThatIncludes({a:'apple', b:'banana'}));
	myobj.fn({a:'apple'});
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.regex(/^[0-9]+/));
	myobj.fn("abc");
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(is.anyOf(["a", "b", "c"]));
	myobj.fn("d");
	mock.verifyAll();
	mock.reset();	
	
	mock.modify().args(is.allOf([is.not(null), is.not(undefined)]));
	myobj.fn(null);
	myobj.fn(undefined);
	mock.verifyAll();
	mock.reset();
		
	var isEven = function(arg) {
		return arg % 2 == 0 ;
	};
	mock.modify().args(is.custom(isEven)).multiplicity(2);
	myobj.fn(3);
	myobj.fn(2);
	mock.verifyAll();
	mock.reset();
		
});
assertFailure(function() {
	var expected = [];
	expected[0] = unos +
		"&rarr;0 (1) fn(is.not('x'))" +br;
	expected[1] = unos +		
		"&rarr;0 (1) fn(is.instanceOf(MyObject))" +br+
		"&rarr;0 (1) fn(is.instanceOf(Array))" +br;	
	expected[2] = unos +	
		"&rarr;0 (1) fn(is.objectThatIncludes({a:'apple',b:'banana'}))" + br+
		noex+
		"fn({a:'apple'})"+ br;
	expected[3] = unos +	
		"&rarr;0 (1) fn(is.regex(/^[0-9]+/))" + br +
		noex+
		"fn('abc')" + br;
	expected[4] = unos +
		"&rarr;0 (1) fn(is.anyOf(['a','b','c']))" + br + 
		noex+
		"fn('d')" + br;
	expected[5] = unos +
		"&rarr;0 (1) fn(is.allOf([is.not(null),is.not(undefined)]))" + br + 
		noex+
		"fn(null)" + br +
		"fn(undefined)" + br;		
	expected[6] = unos +
		"&rarr;1 (2) fn(is.custom(function))" + br + 
		noex+
		"fn(3)" + br;			
	assertFailMessages(expected);
});

jqUnit.test("expectation resolution", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	
	// this is RMock behaviour
	mock.modify().multiplicity(expect.atLeast(2)).args("x"); // atLeast will always match...
	mock.modify().multiplicity(2).args(is.not("y"));         // meaning this will never get matched
	myobj.fn("x");
	myobj.fn("x");
	myobj.fn("x");
	myobj.fn("x");
	mock.verifyAll();
	mock.reset();
	
	// even without verifying unexpected invocations, should fail
	mock.modify().args("x").multiplicity(2);
	myobj.fn("x");
	myobj.fn("x");
	myobj.fn("x");  // 3rd invoc, should fail
	myobj.fn("y");  // since using verify(), should ignore this one
	mock.verify();	
});
assertFailure(function() {
	var expected = [];
	expected[0] = unos +
		s3 + "4 (2..*) fn('x')" + br +
		"&rarr;0 (2) fn(is.not('y'))" + br;
	expected[1] = noex2 + "fn('x')" + br;
	assertFailMessages(expected);
});




jqUnit.test("ordered expectations", function() {

	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	mock.setOrdered(true);
	
	mock.modify().args(1);
	mock.modify().args(2);
	mock.modify().args(3);
	myobj.fn(1);
	myobj.fn(3);
	myobj.fn(2);
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args('x');
	mock.modify().args('y');
	mock.modify().args('x');
	myobj.fn('x');
	myobj.fn('x');
	myobj.fn('y');
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args(1);
	mock.modify().args(2);
	mock.modify().args(3);
	myobj.fn(1);
	myobj.fn(3);
	myobj.fn(2);
	mock.verify();  // verify() only
	mock.reset();			
});
assertFailure(function() {
	var expected = [];
	expected[0] = onos +
		s3 + "1 (1) fn(1)" + br +
		"&rarr;0 (1) fn(2)" + br+
		"&rarr;0 (1) fn(3)" + br+
		noex+
		"fn(3)" + br+
		"fn(2)" + br;
	expected[1] = onos +
		s3 + "1 (1) fn('x')" + br +
		"&rarr;0 (1) fn('y')" + br+
		"&rarr;0 (1) fn('x')" + br+
		noex+
		"fn('x')" + br+
		"fn('y')" + br;		
	expected[2] = onos +
		s3 + "1 (1) fn(1)" + br +
		"&rarr;0 (1) fn(2)" + br+
		"&rarr;0 (1) fn(3)" + br;	
	assertFailMessages(expected);
});


jqUnit.test("using verify() does not print out unexpected section", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");
	
	var objActual = {a:3, fn:function(){}, b:{x:'x',y:'y'}};
	var objExpect = {b:{x:'x',y:'zzzzz'}, a:3};
	mock.modify().args(objExpect);
	mock.modify().args(1,2,3);
	mock.modify().args();
	
	myobj.fn(objActual);
	myobj.fn(1,2);
	myobj.fn('foo');
			
	mock.verify();
});
assertFailure(function() {
	var expected = [];
	expected[0] = unos +
		"&rarr;0 (1) fn({b:{x:'x',y:'zzzzz'},a:3})" +br+
		"&rarr;0 (1) fn(1,2,3)"+br+
		"&rarr;0 (1) fn()"+br;
	assertFailMessages(expected);
});


jqUnit.test("object assertion", function() {
	jqMock.assertThat(1,3);
	jqMock.assertThat(3,"3");
	jqMock.assertThat("b","a");
	jqMock.assertThat([1,2,3],[1,2]);
	jqMock.assertThat([1,2,3],[3,2,1]);
	jqMock.assertThat([1,2,3],[1,2,"x"]);	
	jqMock.assertThat([1,2,3,4,5,6,7],[1,2,"x",4,5,6,7]);
	jqMock.assertThat([1,2,3,4,5,6,7],[1,2,"3",4,5,6,7]);
	jqMock.assertThat({a:'a',b:'b',c:'c'},{a:'a',b:'b'});
	var objActual = {a:3, fn:function(){}, b:{x:'x',y:'y'}};
	var objExpect = {b:{x:'x',y:'zzzzz'}, a:3};
	jqMock.assertThat(objActual,objExpect);
	jqMock.assertThat(3, is.instanceOf(Array));
});
assertFailure(function() {
	var expected = [
		"object did not match expectation, actual: 1, expected: 3",
		"object did not match expectation, actual: 3, expected: '3'",
		"object did not match expectation, actual: 'b', expected: 'a'",
		"object did not match expectation, actual: [1,2,3], expected: [1,2]",
		"object did not match expectation, actual: [1,2,3], expected: [3,2,1]",
		"object did not match expectation, actual: [1,2,3], expected: [1,2,'x']",
		"object did not match expectation, actual: [1,2,3,4,5,6,7], expected: [1,2,'x',4,5,6,7]",
		"object did not match expectation, actual: [1,2,3,4,5,6,7], expected: [1,2,'3',4,5,6,7]",
		"object did not match expectation, actual: {a:'a',b:'b',c:'c'}, expected: {a:'a',b:'b'}",
		"object did not match expectation, actual: {a:3,fn:function(),b:{x:'x',y:'y'}}, expected: {b:{x:'x',y:'zzzzz'},a:3}",
		"object did not match expectation, actual: 3, expected: is.instanceOf(Array)"
	];
	assertFailMessages(expected);
});	


  
  
jqUnit.test("multiplicity", function() {
	var myobj = {fn:function(){}};
	var mock = new jqMock.Mock(myobj, "fn");

	mock.modify().args('x');
	mock.modify().args('y');
	myobj.fn('x');
	myobj.fn('x');
	myobj.fn('y');
	mock.verifyAll();
	mock.reset();
	
	mock.modify().args({a:'alice',b:'bob'});
	mock.modify().args("foobar").multiplicity(expect.atLeast(3));
	mock.modify().args("foo").multiplicity(expect.atMost(1));
	mock.modify().args('a','b', 'c').multiplicity(expect.times(2,3));
	mock.modify().args(123).multiplicity(0);
	
	for (var i=0; i<2; i++) myobj.fn({a:'alice',b:'bob'});
	for (var i=0; i<2; i++) myobj.fn('foobar');
	for (var i=0; i<2; i++) myobj.fn('foo');
	for (var i=0; i<4; i++) myobj.fn('a','b', 'c');
	myobj.fn(123);
	mock.verifyAll();
	mock.reset();
		
});
assertFailure(function() {
	var expected = [];
	expected[0] = noex+
		"fn('x')" + br;
	expected[1] = unos +
		s3 + "1 (1) fn({a:'alice',b:'bob'})" + br +
		"&rarr;2 (3..*) fn('foobar')" + br+
		s3 + "1 (0..1) fn('foo')" + br +
		s3 + "3 (2..3) fn('a','b','c')" + br +
		"&rarr;1 (0..0) fn(123)" + br +
		noex+
		"fn({a:'alice',b:'bob'})" + br+
		"fn('foo')" + br +		
		"fn('a','b','c')" + br;
	assertFailMessages(expected);
});


jqUnit.test("expectThatExceptionThrown", function() {
	jqMock.expectThatExceptionThrown(function() {
		//noop
	}, "something");
	
	jqMock.expectThatExceptionThrown(function() {
		throw "foo";
	}, "bar");
	
	jqMock.expectThatExceptionThrown(function() {
		throw new Error("bad exception 2");
	}, is.exception("bad exception 3"));
	
	// just that some expression is thrown
	jqMock.expectThatExceptionThrown(function() {
		//noop
	}, is.exception());		
});
assertFailure(function() {
	var expected = [
		"The expected exception was never thrown. Expression was: 'something'",
		"expected exception matching 'bar' to be thrown: object did not match expectation, actual: 'foo', expected: 'bar'",
		"expected exception matching is.exception({message:'bad exception 3'}) to be thrown: object did not match expectation, actual: {name:'Error',message:'bad exception 2'}, expected: is.exception({message:'bad exception 3'})",
		"The expected exception was never thrown. Expression was: is.exception()"
	];
	assertFailMessages(expected);
});
